<?php

namespace Wechat;

use app\admin\model\Config;
use app\model\UserOauth;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Form\File;
use EasyWeChat\Kernel\Form\Form;
use EasyWeChat\MiniApp\Application;
use Exception;
use GuzzleHttp\Client;
use libs\Redis;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use util\Logger;

class MiniApp
{
    private static $instance;
    private Application $app;
    private array $config;

    /**
     * @throws Exception
     */
    private function __construct()
    {
        try {
            $db_config    = Config::where('group', 'wx_xcx')->column('name, value');
            $this->config = $db_config;
            $config       = [
                'app_id'                  => $db_config['wx_xcx_appid'],
                'secret'                  => $db_config['wx_xcx_secret'],
                'token'                   => '',
                'aes_key'                 => '',
                'use_stable_access_token' => true,
                'http'                    => [
                    'throw'   => true, // 状态码非 200、300 时是否抛出异常，默认为开启
                    'timeout' => 5.0,
                    'retry'   => true, // 使用默认重试配置
                ],
            ];
            $this->app    = new Application($config);
        } catch (Exception $e) {
            throw new Exception('获取配置失败');
        }
    }

    public static function getInstance(): self
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getApp()
    {
        return $this->app;
    }

    /**
     * 获取微信服务
     * @throws \Throwable
     */
    public function getServer()
    {
        try {
            return $this->app->getServer();
        } catch (\Exception $e) {
            Logger::error('获取微信服务失败: ' . $e->getMessage());
            throw new Exception('获取微信服务失败');
        }
    }

    /**
     * 获取用户Session信息
     * @param string $code
     * @return array
     * @throws Exception
     */
    public function getSession(string $code): array
    {
        try {
            return $this->app->getUtils()->codeToSession($code);
        } catch (\Exception $e) {
            Logger::error('微信登录失败: ' . $e->getMessage(), ['code' => $code]);
            throw new Exception('微信登录失败');
        }
    }

    /**
     * 获取用户Session信息
     * @param string $code
     * @return array
     * @throws Exception
     */
    public function getPhoneNumber(string $code): array
    {
        try {
            $res = $this->app->getClient()->postJson("/wxa/business/getuserphonenumber", ['code' => $code])->toArray();
            if ($res['errcode'] != 0) {
                throw new \Exception($res['errmsg']);
            }
            return $res['phone_info'];
        } catch (\Exception $e) {
            Logger::error('获取手机号失败: ' . $e->getMessage(), ['code' => $code]);
            throw new Exception('获取手机号失败');
        }
    }

    /**
     * 获取数据库的用户session_key， 如果检验登录态失败将重置
     * @param int $user_id
     * @return string
     */
    public function getDbSessionKey(int $user_id)
    {
        //更新授权信息
        $oauth = UserOauth::field('openid, access_info')->where('user_id', $user_id)->find();
        if (empty($oauth['access_info'])) {
            throw new Exception('用户未登录授权');
        }
        $access_info = json_decode($oauth['access_info'], true);
        $openid      = $oauth['openid'];
        $session_key = $access_info['login']['session_key'];
        $signature   = hash_hmac('sha256', "", $session_key);
        try {
            $token  = (new MyAccessToken())->getToken();
            $params = [
                'openid'     => $openid,
                'signature'  => $signature,
                'sig_method' => 'hmac_sha256'
            ];
            $res    = $this->app->getClient()->get("/wxa/checksession", $params)->toArray(false);
            if ($res['errcode'] == 0) {
                return $session_key;
            }
            $reset_res = $this->app->getClient()->withAccessToken((new MyAccessToken()))->get("/wxa/resetusersessionkey", $params)->toArray(false);
            if ($reset_res['errcode'] != 0) {
                throw new Exception($reset_res['errmsg']);
            }
            $new_session_key                     = $reset_res['session_key'];
            $access_info['login']['session_key'] = $new_session_key;
            UserOauth::where('openid', $openid)->update([
                'access_info' => json_encode($access_info, JSON_UNESCAPED_UNICODE),
            ]);
            return $new_session_key;

        } catch (\Exception $e) {
            Logger::error('获取登录态失败: ' . $e->getMessage());
            Redis::getRedis()->del('wx_xcx_access_token');
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 解密用户数据
     * @param string $sessionKey
     * @param string $iv
     * @param string $encryptedData
     * @return array
     * @throws Exception
     */
    public function decryptData(string $sessionKey, string $iv, string $encryptedData): array
    {
        try {
            return $this->app->getUtils()->decryptSession($sessionKey, $iv, $encryptedData);
        } catch (Exception $e) {
            Logger::error('数据解密失败: ' . $e->getMessage(), ['sessionKey' => $sessionKey, 'iv' => $iv, 'encryptedData' => $encryptedData]);
            throw new Exception('用户数据解密失败');
        }
    }

    public function login(string $code)
    {
        try {
            $oauth = $this->app->getOauth();
            // 获取 OAuth 授权用户信息
            $user = $oauth->userFromCode($code);
            return $user->getRaw();
        } catch (InvalidArgumentException $e) {
            Logger::error('微信登录失败: ' . $e->getMessage(), ['code' => $code]);
            throw new Exception('微信登录失败');
        }
    }

    /**
     * 生成小程序码
     * @throws Exception
     */
    public function createQRCode(string $path, string $scene, int $width = 430)
    {
        try {
            $res = $this->app->getClient()->postJson('wxa/getwxacodeunlimit', [
                'path'  => $path,
                'scene' => $scene,
                'width' => $width
            ]);
            // 获取二进制图片内容
            $imageData = $res->getContent();
            if (str_contains($res->getHeaderLine('Content-Type'), 'image/jpeg')) {
                return $imageData;
            }
            return '';
        } catch (TransportExceptionInterface $e) {
            Logger::error('小程序码生成失败: ' . $e->getMessage(), ['path' => $path, 'scene' => $path, 'width' => $width]);
            throw new Exception('小程序码生成失败');
        }
    }

}
